#ifndef TB_REFLECTION_SIMPLE_JSON_WRITER_H__
#define TB_REFLECTION_SIMPLE_JSON_WRITER_H__

#include <stack>

#include <rapidjson/prettywriter.h>
#include <rapidjson/document.h>

#include "../../base/basic_types.hpp"
#include "../../base/string.hpp"
#include "../../base/tostring.hpp"
#include "../../base/unique_id.hpp"
#include "../../reflection/type_info.hpp"

TB_NAMESPACE_BEGIN

template <typename OutputStream, 
          typename SourceEncoding = RAPIDJSON_NAMESPACE::UTF8<TCHAR>, 
          typename TargetEncoding = RAPIDJSON_NAMESPACE::UTF8<TCHAR>,
          typename StackAllocator = RAPIDJSON_NAMESPACE::CrtAllocator >
class JsonWriter : 
  public RAPIDJSON_NAMESPACE::PrettyWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> {
 public:
	 typedef RAPIDJSON_NAMESPACE::MemoryPoolAllocator<> AllocatorType;

   typedef RAPIDJSON_NAMESPACE::CrtAllocator StackAllocatorType;

   typedef RAPIDJSON_NAMESPACE::UTF8<TCHAR> Encoding;

   typedef RAPIDJSON_NAMESPACE::GenericValue<Encoding> ValueType;

   typedef ValueType::StringRefType StringRefType;

  private:
   template <typename T>
   ValueType ValueWrapper(const T& v) {
     return ValueType(v);
   }

   template <>
   ValueType ValueWrapper(const std::string& v) {
     TB::String str = TB_STRING(v);
		 return ValueType(str.data(), (RAPIDJSON_NAMESPACE::SizeType)str.size(), *allocator_);
   }

   template <>
   ValueType ValueWrapper(const string16& v) {
		 std::string str = UTF16ToUTF8(v);
		 return ValueType(str.data(), (uint32)str.size(), *allocator_);
   }

   template <>
   ValueType ValueWrapper(const DateTime& v) {
     return ValueType(v.TotalMilliseconds());
   }

   template <>
   ValueType ValueWrapper(const Duration& v) {
     return ValueType(v.TotalMilliseconds());
   }

	 template <>
	 ValueType ValueWrapper(const UniqueId& v) {
		 TB::String str = TB_STRING(v.ToString()); 
		 return ValueType(str.data(), (uint32)str.size(), *allocator_);;
	 }

 public:
  JsonWriter(OutputStream& os) 
    : PrettyWriter(os), 
      allocator_(RAPIDJSON_NEW(AllocatorType())), 
      stack_(nullptr, kDefaultStackCapacity) {}

  ~JsonWriter() {
    RAPIDJSON_DELETE(allocator_);
  }

  void WriteStructBegin(const reflection::TypeInfo* info, uint32 size) {
    ValueType* object = new (stack_.template Push<ValueType>()) ValueType(RAPIDJSON_NAMESPACE::kObjectType);

		TB::String typeName = info->name;
		object->AddMember(_T("Type"), ValueType(typeName.c_str(), (uint32)typeName.size(), *allocator_), *allocator_);
    object->AddMember(_T("Flag"), (int)Variant::kObjectDataFlag, *allocator_);
    object->AddMember(_T("Size"), (unsigned)size, *allocator_);

    new (stack_.template Push<ValueType>()) ValueType(RAPIDJSON_NAMESPACE::kArrayType);
  }

  void WriteStructEnd() {
    WriteArrayEnd();
  }

  void WriteFieldBegin(const reflection::Property* info) {
    ValueType* arr = stack_.template Top<ValueType>();
    TB_ASSERT(arr && arr->IsArray());
		arr->PushBack(ValueType(info->name.c_str(), (uint32)info->name.size(), *allocator_), *allocator_);
  }

  void WriteFieldEnd() {}

	void WritePairBegin() {
		WriteMapBegin(1);
	}

	void WritePairEnd() {
		WriteArrayEnd();
	}

  void WriteArrayBegin(uint32 size) {
    ValueType* object = new (stack_.template Push<ValueType>()) ValueType(RAPIDJSON_NAMESPACE::kObjectType);

    object->AddMember(_T("Flag"), (int)Variant::kArrayDataFlag, *allocator_);
    object->AddMember(_T("Size"), size, *allocator_);

    new (stack_.template Push<ValueType>()) ValueType(RAPIDJSON_NAMESPACE::kArrayType);
  }

	void WriteArrayEnd() {
		ValueType* value = stack_.template Pop<ValueType>(1);
		TB_ASSERT(value && value->IsArray());

		ValueType* object = stack_.template Pop<ValueType>(1);
		TB_ASSERT(object && object->IsObject());

		object->AddMember(_T("Value"), *value, *allocator_);

		bool topLevel = true;
		if (stack_.GetSize() > 0) {
			ValueType* arr = stack_.template Top<ValueType>();
			if (arr->IsArray()) {
				arr->PushBack(*object, *allocator_);
				topLevel = false;
			}
		}

		if (topLevel) {
			// last pop operation leave the element at current pos unchanged
			stack_.template Push<ValueType>(1);
		}
	}

  void WriteMapBegin(uint32 size) {
    ValueType* object = new (stack_.template Push<ValueType>()) ValueType(RAPIDJSON_NAMESPACE::kObjectType);

    object->AddMember(_T("Flag"), (int)Variant::kMapDataFlag, *allocator_);
    object->AddMember(_T("Size"), size, *allocator_);

    new (stack_.template Push<ValueType>()) ValueType(RAPIDJSON_NAMESPACE::kArrayType);
  }

	void WriteMapEnd() {
		WriteArrayEnd();
	}

  template <typename T> 
  bool WriteValue(const T& val) {
    uint32 typeId = GetVariantTypeId<T>();
		if (const reflection::TypeInfo* info = reflection::GetTypeInfo(typeId)) {
      if (stack_.Empty()) {
          TB_ASSERT(Variant::IsBuildinTypeId(typeId));
          ValueType* object = new (stack_.template Push<ValueType>()) ValueType(ValueWrapper(val));
      } else {
        ValueType* arr = stack_.template Top<ValueType>();
        TB_ASSERT(arr && arr->IsArray());
        arr->PushBack(ValueWrapper(val), *allocator_);
      }
			return true;
    }

    return false;
  }

  //! flush to output stream
  void Flush() {
    if (stack_.GetSize() == sizeof(ValueType)) {
      stack_.template Pop<ValueType>(1)->Accept(*this);
    } else {
      TB_ASSERT(false && "should not reached");
    }
  }

  ValueType* GetValue() const {
    if (stack_.GetSize() == sizeof(ValueType)) {
      return stack_.template Top<ValueType>();
    } else {
      return nullptr;
    }
  }

 private:
  static const size_t kDefaultStackCapacity = 1024;
  RAPIDJSON_NAMESPACE::internal::Stack<StackAllocatorType> stack_;
  AllocatorType* allocator_;
};

TB_NAMESPACE_END

#endif // TB_REFLECTION_SIMPLE_JSON_WRITER_H__
